/**
 * 该文件（`packages/core-common/src/event-bus/event-bus.ts`）实现了一个事件总线（EventBusImpl），用于在系统内不同模块间分发和监听事件。核心逻辑如下：
 *
 * 1. **事件注册与触发**
 *     - 通过 `on` 方法注册事件监听器，支持按事件类型（构造器）监听。
 *     - `fire` 方法触发事件，会调用所有注册的监听器。
 *     - `once` 方法注册一次性监听器，事件触发后自动移除。
 *
 * 2. **异步事件支持**
 *     - `fireAndAwait` 方法支持异步事件触发，等待所有监听器处理完毕（带超时）。
 *
 * 3. **指令事件**
 *     - `onDirective` 和 `fireDirective` 支持通过字符串指令注册和触发事件，适合动态事件场景。
 *
 * 4. **内部实现**
 *     - 用 `emitterMap`（Map）缓存每种事件类型或指令的 Emitter 实例，保证同一类型事件共用同一个 Emitter。
 *     - `getOrCreateEmitter` 工具方法确保 Emitter 存在或自动创建。
 *
 * **总结**：
 * 本文件为系统提供了统一、灵活的事件分发与监听机制，支持同步/异步、类型/指令多种事件模型，便于模块解耦和扩展。
 */
import { BasicEvent } from './basic-event';
import {
  IAsyncEventFireOpts,
  IEventBus,
  IEventFireOpts,
  IEventListener,
} from './event-bus-types';
import { Emitter, IAsyncResult, IDisposable, Event } from '../../utils';
import { ConstructorOf } from '../../utils/declare';

export class EventBusImpl implements IEventBus {
  private emitterMap = new Map<any, Emitter<any>>();

  fire<T extends BasicEvent<any>>(e: T, opts: IEventFireOpts = {}) {
    const Constructor = e && e.constructor;
    if (
      typeof Constructor === 'function' &&
      BasicEvent.isPrototypeOf(Constructor)
    ) {
      const emitter = this.emitterMap.get(Constructor);
      if (emitter) {
        emitter.fire(e);
      }
    }
  }

  async fireAndAwait<T extends BasicEvent<any>, R>(
    e: T,
    opts: IAsyncEventFireOpts = { timeout: 2000 }
  ): Promise<IAsyncResult<R>[]> {
    const Constructor = e && e.constructor;
    if (
      typeof Constructor === 'function' &&
      BasicEvent.isPrototypeOf(Constructor)
    ) {
      const emitter = this.emitterMap.get(Constructor);
      if (emitter) {
        return emitter.fireAndAwait<R>(e, opts.timeout);
      }
    }
    return [];
  }

  on<T>(Constructor: ConstructorOf<T>, listener: IEventListener<T>) {
    const emitter = this.getOrCreateEmitter(Constructor);
    return emitter.event(listener);
  }

  once<T>(Constructor: ConstructorOf<T>, listener: IEventListener<T>) {
    const emitter = this.getOrCreateEmitter(Constructor);
    return Event.once(emitter.event)(listener);
  }

  private getOrCreateEmitter(key: any) {
    const current = this.emitterMap.get(key);
    if (current) {
      return current;
    }

    const emitter = new Emitter();
    this.emitterMap.set(key, emitter);
    return emitter;
  }

  onDirective<T>(directive: string, listener: IEventListener<T>): IDisposable {
    const emitter = this.getOrCreateEmitter(directive);
    return emitter.event(listener);
  }

  fireDirective(directive: string, payload: any) {
    const emitter = this.emitterMap.get(directive);
    if (emitter) {
      emitter.fire(payload);
    }
  }
}
